/**
 * Copyright 2010 Butin Kirill <kiryaka@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.flexupload.strategy
{
	import flash.net.FileReference;
	import flash.utils.setInterval;
	import flash.utils.setTimeout;
	
	import mx.collections.ArrayCollection;
	import mx.messaging.ChannelSet;
	import mx.messaging.channels.AMFChannel;
	
	import org.flexupload.remote.FlexuploadService;
	import org.flexupload.remote.ServerSimulationService;
	import org.flexupload.status.FileStatus;
	import org.flexupload.status.UploadStatus;
	import org.flexupload.stream.FileStream;
	
	public class DevStrategy extends Strategy
	{
		private var uploadStatus:UploadStatus;
		private var byteArrayLength:int = 20*1024; // FilePart ByteArray length in bytes (100*1024 = 100Kb)
		private var bigFile:FileStatus = null;
		
		private var fileStreams:ArrayCollection = new ArrayCollection();
		private var freeServices:Array = new Array();
		private var servicesCount:int = 0;
		
		private var serverURL:String = "local";
		private var serverDestination:String;
		private var serverSource:String;

		private var controlService:FlexuploadService;
		
		private var maxThreadsPerFile:int = 5;
		private var maxServices:int = 18;

		public function DevStrategy(){}
		
		public function start():void{
			setInterval(assignFreeSessions, 1000);
			createRemoteService();
			controlService = getService();
			for (var i:int = 0; i < maxServices; i++)
				createRemoteService();			
		}
		
		public function setUploadStatus(uploadStatus:UploadStatus):void{
			this.uploadStatus = uploadStatus;
		}
		
		private function createRemoteService():void{
			if (!canWeCreateNewService()) return;
			// TODO refactoring it should be one class with factory methods instead of 2.
			var remoteService:FlexuploadService = serverURL == "local" ? getServerSimulationService() : getRealService();
			servicesCount++;
			markServiceAsFree(remoteService);		
		}
		
		private function getServerSimulationService():FlexuploadService{
			var remoteService:ServerSimulationService = new ServerSimulationService();
			remoteService.setStrategy(this);
			return remoteService;
		}		
		private function getRealService():FlexuploadService{
			var channelSet:ChannelSet = new ChannelSet();
			channelSet.addChannel(new AMFChannel(servicesCount.toString(), serverURL));
			var remoteService:FlexuploadService = new FlexuploadService(serverDestination);
			if (serverSource) remoteService.source = serverSource;
			remoteService.channelSet = channelSet;
			remoteService.setStrategy(this);
			return remoteService;
		}
		
		public function canStartNewFile():Boolean{
			if (isUploadBigFileNow()) return false;
			if (fileStreams.length == 0) return true;
			
			var bytes:uint = 0; // bytes in progress now (amount of not uploaded bytes in all working files)
			for each(var fileStream:FileStream in fileStreams){
				bytes += fileStream.getFileStatus().getSize()-fileStream.getFileStatus().getBytesTransfered();
			}
			// if we finish out current upload less than 10 seconds - start new
			return (freeServices.length) > 0 && (bytes/uploadStatus.getCurrentSpeed() < 10);
			
		}
		
		private function isUploadBigFileNow():Boolean{
			if (bigFile && !bigFile.isFinished()) return true;
			bigFile = null;
			return false;
		}
		
		public function getCurentSpeed():int{
			return uploadStatus.getCurrentSpeed();
		}			
		
		public function getCurentSpeedPerThread():int{
			return getCurentSpeed()/getActiveServicesCount();
		}
		
		public function startUploadFile(file:FileReference):void{			
			var fileStream:FileStream = new FileStream();
			fileStreams.addItem(fileStream);
			fileStream.start(file, getControlService());
			
			// if we will upload file more than 20 seconds with our current speed - mark file as big (no more files until this one will be complete)
			// ignore value of cuurent speed less than Kbit 
			var currentSpeed:int = getCurentSpeed();
			if (currentSpeed > 1000 && file.size/getCurentSpeed() > 20){
				bigFile = fileStream.getFileStatus();
			}
		}
		
		public function markServiceAsFree(remoteService:FlexuploadService):void{
			freeServices.push(remoteService);
		}
		
		public function assignFreeSessions():void{
			// TODO refactoring??
			if (freeServices.length == 0) return;
			calculateByteArrayLength();
			for(var i:int = 0; i<fileStreams.length; i++){
				var fileStream:FileStream = FileStream(fileStreams[i]);
				var fileStatus:FileStatus = fileStream.getFileStatus();
				
				// if file is finished - remove from list and continue
				if (fileStatus.isFinished()){
					fileStreams.removeItemAt(i);
					continue;
				}
				// If file not started yet - skip
				if (!fileStatus.isProcessing() && freeServices.length > 0) continue;
				
				// if file havn't thread and we have free - start
				if(fileStatus.getThreadCount() == 0) 
						return fileStream.createNewThread(getService());
				
				if (fileStatus.getThreadCount() < maxThreadsPerFile && 	// if this file dont have max tred yet 
					getCurentSpeedPerThread() > 0 && 					// if current speed already calculated and in according with it  
					(fileStatus.getSize()-fileStatus.getBytesTransfered())/(getCurentSpeedPerThread()*fileStatus.getThreadCount()) > 10) //file will be uploaded more than 10 seconds 
				{
					return fileStream.createNewThread(getService());
				}	
			}
		}
		
		private function calculateByteArrayLength():void{
			byteArrayLength = 60000;
			return			
			// todo bytearray strategy
			if (serverURL == "local"){
				byteArrayLength = 50*1024;
				return;
			}  
			var nowVal:int = getCurentSpeedPerThread();
			if (nowVal < 20*1024){
				byteArrayLength = 30*1024;
				return
			}
			if (nowVal > 1024*1024){
				byteArrayLength = 1024*1024;
				return
			}			

			byteArrayLength = nowVal;
		}
		
		public function getByteArrayLength():int{
			return byteArrayLength;
		}		
		
		public function getService():FlexuploadService{
			return freeServices.pop();
		}
		
		public function getControlService():FlexuploadService{
			return controlService;
		}
		
		public function getActiveServicesCount():int{
			return servicesCount - freeServices.length - 1;
		}
		
		public function getActiveFilesCount():int{
			return fileStreams.length;
		}		
		
		public function canWeCreateNewService():Boolean{
			return (maxServices - servicesCount) > 0;
		}
		
		public function setServerUrl(serverURL:String, serverDestination:String, serverSource:String = null):void{
			this.serverURL = serverURL;
			this.serverDestination = serverDestination;
			this.serverSource = serverSource;
		}		
	}
}